home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / FloatMenuLib / FloatMenuLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-16  |  17.3 KB  |  597 lines  |  [TEXT/KAHL]

  1. /* display a tear-off menu in a windoid
  2.     94/01/09 aih removed kMenuMargin constant
  3.     93/12/01 aih created */
  4.  
  5. #include "AsmLib.h"
  6. #include "DrawLib.h"
  7. #include "FloatMenuLib.h"
  8. #include "LowMemLib.h"
  9. #include "MathLib.h"
  10. #include "MacLib.h"
  11. #include "MemoryLib.h"
  12. #include "MenuLib.h"
  13. #include "RectangleLib.h"
  14. #include "ResourceConstantsLib.h"
  15. #include "WindoidWDEF.h"
  16. #include "WindowLib.h"
  17.  
  18. /* message sent to mdef when there's a click in a floating menu's window */    
  19. #define mFloatChooseMsg        (128)
  20.  
  21. /* margin around menu in which not to tear off menu */
  22. #define TEAR_MARGIN            (7)
  23.  
  24. /* glue for calling our menu definition function */
  25. typedef struct {
  26.     short jmp;                    /* jump instruction */
  27.     MenuProcPtr proc;            /* address of original mdef function */
  28.     FloatMenuHandle fmenu;    /* the floating menu */
  29. } FloatMenuGlueType, *FloatMenuGluePtr, **FloatMenuGlueHandle;
  30.  
  31. /* list of all floating menus */
  32. static FloatMenuHandle gFloatMenuList;
  33.  
  34. static void FloatMenuDrawDirect(FloatMenuHandle fmenu);
  35. static void FloatMenuDrawHook(OffscreenHandle offscreen, void *data);
  36.  
  37. /*----------------------------------------------------------------------------*/
  38. /* calling menu definition functions */
  39. /*----------------------------------------------------------------------------*/
  40.  
  41. static void mdef_call(Handle mdef, short msg, MenuHandle menu,
  42.     Rect *bounds, Point hit, short *which)
  43. {
  44.     SignedByte state = HandleLock(mdef);
  45.     ((MenuProcPtr) *mdef)(msg, menu, bounds, hit, which);
  46.     HandleRestore(mdef, state);
  47. }
  48.  
  49. static void mdef(short msg, MenuHandle menu, Rect *bounds,
  50.     Point hit, short *which)
  51. {
  52.     mdef_call((**menu).menuProc, msg, menu, bounds, hit, which);
  53. }
  54.  
  55. static void mdef_draw(MenuHandle menu, Rect *bounds)
  56. {
  57.     Point hit = { 0, 0 };
  58.     short which = 0;
  59.     
  60.     mdef(mDrawMsg, menu, bounds, hit, &which);
  61. }
  62.  
  63. static void mdef_choose(MenuHandle menu, Rect *bounds, Point hit, short *which)
  64. {    
  65.     mdef(mChooseMsg, menu, bounds, hit, which);
  66. }
  67.  
  68. static void mdef_size(MenuHandle menu)
  69. {
  70.     Rect bounds = { 0, 0, 0, 0 };
  71.     Point hit = { 0, 0 };
  72.     short which = 0;
  73.     
  74.     mdef(mSizeMsg, menu, &bounds, hit, &which);
  75. }
  76.  
  77. /*----------------------------------------------------------------------------*/
  78. /* menu definition for a floating window */
  79. /*----------------------------------------------------------------------------*/
  80.  
  81. /* drag a gray outline of the menu, return coordinates for torn off menu */
  82. static Point FloatMenuDrag(FloatMenuHandle fmenu, const Rect *bounds,
  83.     Point startPt)
  84. {
  85.     Rect content, structure, drag, new, contRect;
  86.     RgnHandle contRgn = NULL;
  87.     Point pos;
  88.     
  89.     require(FloatMenuValid(fmenu));
  90.     
  91.     /* calculate rectangles */
  92.     if ((**fmenu).window) {
  93.         /* we have a window, so get the rectangles from it */
  94.         WinRectangles((**fmenu).window, &structure, &content, &drag, &new);
  95.     }
  96.     else {
  97.         /* No window yet, so calculate the rectangles. This doesn't have
  98.             to be 100% accurate, but it should be within 1 or 2 pixels
  99.             of the real values. */
  100.         content.top = startPt.h;
  101.         content.right = startPt.v;
  102.         content.bottom = content.top + RectHeight(bounds);
  103.         content.right = content.left + RectWidth(bounds);
  104.         structure = content;
  105.         InsetRect(&structure, -kWindoidBorderSize, -kWindoidBorderSize);
  106.         structure.top -= kWindoidDragSize + 1;
  107.         structure.right += kWindoidShadowSize;
  108.         structure.bottom += kWindoidShadowSize;
  109.         drag = content;
  110.         drag.top = content.top - kWindoidDragSize - kWindoidBorderSize;
  111.         drag.bottom = content.top - kWindoidBorderSize;
  112.     }
  113.     
  114.     /* calculate region that excludes the menu bar, this menu, and a
  115.         small margin around the menu; if the mouse leaves contRgn
  116.         (short for "continue region") dragging is stopped */
  117.     contRgn = BeginRgn();
  118.     contRect = *bounds;
  119.     InsetRect(&contRect, -TEAR_MARGIN, -TEAR_MARGIN);
  120.     RectRgn(contRgn, &contRect);
  121.     DiffRgn(GetGrayRgn(), contRgn, contRgn);
  122.     
  123.     /* offset rectangles so their top left
  124.         is next to the cursor */
  125.     pos.h = startPt.h - content.left - 5;
  126.     pos.v = startPt.v - content.top + 5;
  127.     OffsetRect(&structure, pos.h, pos.v);
  128.     OffsetRect(&content, pos.h, pos.v);
  129.     OffsetRect(&drag, pos.h, pos.v);
  130.     
  131.     /* drag an outline of the menu */
  132.     pos = DragRect(&structure, &drag, contRgn, GetGrayRgn(), startPt);
  133.     if (pos.h || pos.v) {
  134.         pos.h += content.left;
  135.         pos.v += content.top;
  136.     }
  137.     EndRgn(contRgn);
  138.     return(pos);
  139. }
  140.  
  141. /* task called after a menu has been torn off; positions and shows floating
  142.     menu's window */
  143. static void FloatMenuTearOffTask(TaskHandle task, void *data)
  144. {
  145.     FloatMenuHandle fmenu = data;
  146.     
  147.     require(FloatMenuValid(fmenu));
  148.     require(task == (**fmenu).tearTask);
  149.     EventPostTaskDelete(task);
  150.     (**fmenu).tearTask = NULL;
  151.     FloatMenuOpen(fmenu);
  152.     WinMove((**fmenu).window, (**fmenu).position.h, (**fmenu).position.v);
  153.     if (! WinVisible((**fmenu).window)) {
  154.         WinShow((**fmenu).window);
  155.         WinSelect((**fmenu).window);
  156.     }
  157. }
  158.  
  159. /* track the mouse and install a task to position the torn off menu's window */
  160. static void FloatMenuTearOff(FloatMenuHandle fmenu, const Rect *bounds,
  161.     Point pt)
  162. {
  163.     Point position;
  164.     TaskHandle task = NULL;
  165.     
  166.     require(FloatMenuValid(fmenu));
  167.     GetMouse(&position);
  168.     pt = FloatMenuDrag(fmenu, bounds, pt);
  169.     if (pt.h || pt.v) {
  170.         /* Install a task to be executed after MenuSelect returns.
  171.             If we tried showing or moving the torn-off menu's window
  172.             before MenuSelect returned it would result in weird results
  173.             when MenuSelect tried to restore the saved bits behind the
  174.             menu and they overlapped the window. */
  175.         check(! StillDown());
  176.         check(! (**fmenu).tearTask);
  177.         task = EventPostTaskInsert(FloatMenuTearOffTask, fmenu);
  178.         (**fmenu).tearTask = task;
  179.         (**fmenu).position = pt;
  180.     }
  181.     ensure(FloatMenuValid(fmenu));
  182. }
  183.  
  184. /* menu definition function for a floating menu */
  185. static pascal void FloatMenuMDEF(short msg, MenuHandle menu, Rect *bounds,
  186.     Point hit, short *which)
  187. {
  188.     FloatMenuHandle fmenu = NULL;
  189.     Boolean tearoff = true;
  190.  
  191.     /* get the float menu from the glue handle */
  192.     fmenu = (**(FloatMenuGlueHandle) (**menu).menuProc).fmenu;
  193.     check(FloatMenuValid(fmenu));
  194.     
  195.     if (msg == mFloatChooseMsg) {
  196.         /* We use the message mFloatChooseMsg to distinguish between
  197.             a click in a torn-off menu and a click in a regular menu
  198.             still attached to the menu bar. The message mFloatChooseMsg
  199.             is only sent when there's a click in a torn-off menu, so that
  200.             we shouldn't try to tear off the menu again. */
  201.         tearoff = false;
  202.         msg = mChooseMsg;
  203.     }
  204.     
  205.     /* call the original menu definition function */
  206.     mdef_call((**fmenu).mdef, msg, menu, bounds, hit, which);
  207.     
  208.     /* Tear off menu if appropriate (we're not already torn off, nothing
  209.         was selected from the menu, the menu's enabled, and there's
  210.         sufficient memory so the menu won't be closed immediately due to
  211.         memory shortage). */
  212.     if (msg == mChooseMsg && tearoff && ! *which &&
  213.          MenuEnabled((**fmenu).menu, 0) &&
  214.          MemWarning() < MEM_WARNING_VERY_LOW)
  215.     {
  216.         FloatMenuTearOff(fmenu, bounds, hit);
  217.     }
  218. }
  219.  
  220. /*----------------------------------------------------------------------------*/
  221. /* floating menu functions */
  222. /*----------------------------------------------------------------------------*/
  223.  
  224. /* true if a valid float menu */
  225. Boolean FloatMenuValid(FloatMenuHandle fmenu)
  226. {
  227.     check(sizeof(MenuInfo) > sizeof(Str255));
  228.     if (! HandleValidSize(fmenu, sizeof(FloatMenuType))) return(false);
  229.     if (! HandleValidSize((**fmenu).menu, sizeof(MenuInfo) - sizeof(Str255))) return(false);
  230.     return(true);
  231. }
  232.  
  233. /* create a new float menu for the specified menu; call only once for any
  234.     one menu */
  235. FloatMenuHandle FloatMenuBegin(short id)
  236. {
  237.     volatile FloatMenuHandle fmenu = NULL;    /* the floating menu */
  238.     MenuHandle menu = NULL;    /* the menu */
  239.     Handle copy;                /* for copy of menu data */
  240.     FloatMenuGlueType glue = { ASM_JMP, FloatMenuMDEF, NULL };
  241.  
  242.     require(! FloatMenuFindID(id));
  243.     TRY {
  244.     
  245.         /* create floating menu, add to list of all floating menus */
  246.         fmenu = HandleBeginClear(sizeof(FloatMenuType));
  247.         gFloatMenuList = LLHInsert(gFloatMenuList, fmenu);
  248.         
  249.         /* get menu handle from menu list, or load from resource
  250.             file if not already installed in menu list */
  251.         menu = MenuHandleGet(id);
  252.         if (! menu) {
  253.             menu = GetMenu(id);
  254.             FailNILRes(menu);
  255.             (**fmenu).owner = true;
  256.         }
  257.         HNoPurge((Handle) menu);
  258.         (**fmenu).menu = menu;
  259.         
  260.         /* allocate a handle for the copy of the menu data */
  261.         copy = HandleBegin(0);
  262.         HandlePurge(copy);
  263.         (**fmenu).menuCopy = copy;
  264.         
  265.         /* patch menu def proc so it first calls our function, which allows
  266.             us to track the mouse and install a task to tear off the menu */
  267.         glue.fmenu = fmenu;
  268.         (**fmenu).mdef = (**menu).menuProc;
  269.         HNoPurge((**fmenu).mdef);
  270.         (**menu).menuProc = HandleBegin(sizeof(FloatMenuGlueType));
  271.         BlockMove(&glue, *(**menu).menuProc, sizeof(FloatMenuGlueType));
  272.         
  273.     } CATCH {
  274.         FloatMenuEnd(fmenu);
  275.     } ENDTRY;
  276.     ensure(FloatMenuValid(fmenu));
  277.     return(fmenu);
  278. }
  279.  
  280. /* dispose of the floating menu */
  281. void FloatMenuEnd(FloatMenuHandle fmenu)
  282. {
  283.     if (fmenu) {
  284.         FloatMenuClose(fmenu);
  285.         gFloatMenuList = LLHDelete(gFloatMenuList, fmenu);
  286.         (**(**fmenu).menu).menuProc = (**fmenu).mdef;
  287.         if ((**fmenu).owner) HandleEnd((**fmenu).menu);
  288.         if ((**fmenu).menuCopy) DisposeHandle((**fmenu).menuCopy);
  289.         EventPostTaskDelete((**fmenu).tearTask);
  290.         HandleEnd(fmenu);
  291.     }
  292. }
  293.  
  294. /* create the floating menu's window; has no effect if window already exists */
  295. void FloatMenuOpen(FloatMenuHandle fmenu)
  296. {
  297.     OffscreenHandle offscreen = NULL;
  298.     WindowPtr window = NULL;
  299.  
  300.     require(FloatMenuValid(fmenu));
  301.     if (! (**fmenu).window) {
  302.         window = WinGet(RLW_FLOAT_MENU);
  303.         (**fmenu).window = window;
  304.         offscreen = OffscreenBegin(window, FloatMenuDrawHook, fmenu);
  305.         (**fmenu).offscreen = offscreen;
  306.         WinRegister(window, fmenu, FloatMenuEventTable());
  307.         FloatMenuAdjust(fmenu);
  308.     }
  309.     ensure(FloatMenuValid(fmenu));
  310. }
  311.  
  312. /* dispose of the floating menu's window but don't dispose of the menu itself;
  313.     no effect if already disposed of window */
  314. void FloatMenuClose(FloatMenuHandle fmenu)
  315. {
  316.     require(FloatMenuValid(fmenu));
  317.     OffscreenEnd((**fmenu).offscreen);
  318.     (**fmenu).offscreen = NULL;
  319.     WinUnregister((**fmenu).window, fmenu);
  320.     WinEnd((**fmenu).window);
  321.     (**fmenu).window = NULL;
  322.     FloatMenuForget(fmenu);
  323.     ensure(FloatMenuValid(fmenu));
  324. }
  325.  
  326. /* return the ID of the floating menu */
  327. short FloatMenuID(FloatMenuHandle fmenu)
  328. {
  329.     require(FloatMenuValid(fmenu));
  330.     return((**(**fmenu).menu).menuID);
  331. }
  332.  
  333. /* return the last item selected from the floating menu */
  334. short FloatMenuItem(FloatMenuHandle fmenu)
  335. {
  336.     require(FloatMenuValid(fmenu));
  337.     return((**fmenu).item);
  338. }
  339.  
  340. /* return the floating menu with the specified id */
  341. FloatMenuHandle FloatMenuFindID(short id)
  342. {
  343.     FloatMenuHandle item = NULL;
  344.     
  345.     for (item = gFloatMenuList; item && FloatMenuID(item) != id; item = LLHNext(item))
  346.         ;
  347.     ensure(! item || FloatMenuID(item) == id);
  348.     return(item);
  349. }
  350.  
  351. /* draw the floating menu directly into its port */
  352. static void FloatMenuDrawDirect(FloatMenuHandle fmenu)
  353. {
  354.     Rect bounds;
  355.     GrafPtr port = NULL;
  356.     
  357.     require(FloatMenuValid(fmenu));
  358.     require((**fmenu).window != NULL);
  359.  
  360.     /* setup */
  361.     GetPort(&port);
  362.     SetPort((**fmenu).window);
  363.  
  364.     /* draw menu */
  365.     WinPortRect((**fmenu).window, &bounds);
  366.     EraseRect(&bounds);
  367.     SetTopMenuItem(0);
  368.     SetAtMenuBottom(bounds.bottom);
  369.     mdef_draw((**fmenu).menu, &bounds);
  370.  
  371.     /* gray out items */
  372.     if (! MenuEnabled((**fmenu).menu, 0)) {
  373.         PenState pen;
  374.         GetPenState(&pen);
  375.         PenMode(patBic);
  376.         PenPat(gray);
  377.         PaintRect(&bounds);
  378.         SetPenState(&pen);
  379.     }
  380.     
  381.     SetPort(port);
  382.     ensure(FloatMenuValid(fmenu));
  383. }
  384.  
  385. /* call-back from the offscreen bitmap for drawing the menu */
  386. static void FloatMenuDrawHook(OffscreenHandle offscreen, void *data)
  387. {
  388.     FloatMenuDrawDirect(data);
  389. }
  390.  
  391. /* Draw the floating menu either from an offscreen bitmap or directly to
  392.     the screen if there's no offscreen bitmap. */
  393. void FloatMenuDraw(FloatMenuHandle fmenu)
  394. {
  395.     require(FloatMenuValid(fmenu));
  396.     if ((**fmenu).offscreen)
  397.         OffscreenDraw((**fmenu).offscreen);
  398.     else
  399.         FloatMenuDrawDirect(fmenu);
  400.     ensure(FloatMenuValid(fmenu));
  401. }
  402.  
  403. /* true if data describing the menu's appearance have changed since the
  404.     last time the data were saved */
  405. Boolean FloatMenuChanged(FloatMenuHandle fmenu)
  406. {
  407.     Boolean result = true;
  408.     register char *p, *q;
  409.     register size_t n;
  410.     
  411.     require(FloatMenuValid(fmenu));
  412.     CalcMenuSize((**fmenu).menu);
  413.     if (*(**fmenu).menuCopy) {
  414.         n = HandleSize((**fmenu).menu);
  415.         if (n == HandleSize((**fmenu).menuCopy)) {
  416.             p = *(Handle) (**fmenu).menu;
  417.             q = *(Handle) (**fmenu).menuCopy;
  418.             while (n > 0 && *p++ == *q++)
  419.                 n--;
  420.             if (n == 0)
  421.                 result = false;
  422.         }
  423.     }
  424.     ensure(FloatMenuValid(fmenu));
  425.     return(result);
  426. }
  427.  
  428. /* save a copy of data describing the menu's appearance so it can be
  429.     checked later */
  430. void FloatMenuRemember(FloatMenuHandle fmenu)
  431. {
  432.     Handle copy;
  433.     size_t n;
  434.     
  435.     require(FloatMenuValid(fmenu));
  436.     n = HandleSize((**fmenu).menu);
  437.     MemCheck(n);
  438.     ReallocateHandle((**fmenu).menuCopy, n);
  439.     BlockMove(*(Handle) (**fmenu).menu, *(Handle) (**fmenu).menuCopy, n);
  440.     ensure(! FloatMenuChanged(fmenu));
  441. }
  442.  
  443. /* forget the copy of the data describing the menu's appearance */
  444. void FloatMenuForget(FloatMenuHandle fmenu)
  445. {
  446.     require(FloatMenuValid(fmenu));
  447.     EmptyHandle((Handle) (**fmenu).menuCopy);
  448.     ensure(FloatMenuChanged(fmenu));
  449. }
  450.  
  451. /* if the hiliting, appearance, or size of the menu has changed then the
  452.     window is adjusted and the menu is redisplayed */
  453. void FloatMenuAdjust(FloatMenuHandle fmenu)
  454. {
  455.     Rect    portRect;    /* window's port rectangle */
  456.     Rect    newRect;        /* rectangle for new windows */
  457.     Point size;            /* width and height of window */
  458.  
  459.     require(FloatMenuValid(fmenu));
  460.     require((**fmenu).window != NULL);
  461.     
  462.     /* only adjust if menu's data have changed */
  463.     if (FloatMenuChanged(fmenu)) {
  464.     
  465.         /* calculate size of window */
  466.         WinNewRect((**fmenu).window, &newRect);
  467.         WinPortRect((**fmenu).window, &portRect);
  468.         size.h = min((**(**fmenu).menu).menuWidth, RectWidth(&newRect));
  469.         size.v = min((**(**fmenu).menu).menuHeight, RectHeight(&newRect));
  470.  
  471.         /* disallow long menus since scrolling doesn't work very well */
  472.         check((**(**fmenu).menu).menuHeight <= RectHeight(&newRect));
  473.  
  474.         /* adjust window size */
  475.         if (size.h != RectWidth(&portRect) || size.v != RectHeight(&portRect)) {
  476.             WinSize((**fmenu).window, size.h, size.v);
  477.             WinPortRect((**fmenu).window, &portRect);
  478.             if ((**fmenu).offscreen)
  479.                 OffscreenBoundsSet((**fmenu).offscreen, &portRect);
  480.         }
  481.  
  482.         /* draw menu and remember menu's data */
  483.         if ((**fmenu).offscreen)
  484.             OffscreenChange((**fmenu).offscreen);
  485.         FloatMenuDraw(fmenu);
  486.         FloatMenuRemember(fmenu);
  487.     }
  488.     ensure(! FloatMenuChanged(fmenu));
  489. }
  490.  
  491. /* Adjust display of the floating menus. Checks if the appearance of any of the
  492.     floating menus has changed and must be redrawn. Should be called after all
  493.     the other menu adjust handlers of the application, since those handlers
  494.     are responsible for enabling and disabling the menu items. */
  495. void FloatMenuAdjustAll(void)
  496. {
  497.     FloatMenuHandle fmenu = NULL;
  498.     
  499.     for (fmenu = gFloatMenuList; fmenu; fmenu = LLHNext(fmenu)) {
  500.         if ((**fmenu).window)
  501.             FloatMenuAdjust(fmenu);
  502.     }
  503. }
  504.  
  505. /* unhilite the most recently selected item in the menu */
  506. void FloatMenuUnhilite(FloatMenuHandle fmenu)
  507. {
  508.     Point hit = { -1, -1 };
  509.     Rect bounds;
  510.     short item = 0;
  511.     GrafPtr port = NULL;
  512.     
  513.     require(FloatMenuValid(fmenu));
  514.     require((**fmenu).window);
  515.     GetPort(&port);
  516.     SetPort((**fmenu).window);
  517.     WinPortRect((**fmenu).window, &bounds);
  518.     item = (**fmenu).item;
  519.     SetTopMenuItem(0);
  520.     SetAtMenuBottom(bounds.bottom);
  521.     mdef(mFloatChooseMsg, (**fmenu).menu, &bounds, hit, &item);
  522.     SetPort(port);
  523.     ensure(FloatMenuValid(fmenu));
  524. }
  525.  
  526. /* track the cursor and select an item from the menu */
  527. void FloatMenuChoose(FloatMenuHandle fmenu)
  528. {
  529.     Point hit;                    /* mouse location */
  530.     Rect bounds;                /* bounds of menu */
  531.     short item = 0;            /* selected item */
  532.     long delay = 0;            /* for flashing menu */
  533.     GrafPtr port = NULL;        /* saved port */
  534.     MenuPickType pick;        /* menu selection */
  535.     
  536.     require(FloatMenuValid(fmenu));
  537.     require((**fmenu).window);
  538.  
  539.     /* adjust menus to ensure that items that are supposed to be disabled
  540.         are really disabled, otherwise we might generate a command that the
  541.         application can't handle */
  542.     EventAdjustMenu();
  543.     if (MenuEnabled((**fmenu).menu, 0)) {
  544.  
  545.         /* Select item from menu, using the menu definition function to hilite
  546.             the item that the mouse is over. We pass the menu definition function
  547.             the message 'mFloatChooseMsg' to differentiate choosing from a torn
  548.             off menu from choosing from a menu still attached to the menu bar.
  549.             This prevents us from trying to tear off a menu that has already
  550.             been torn off. */
  551.         GetPort(&port);
  552.         SetPort((**fmenu).window);
  553.         WinPortRect((**fmenu).window, &bounds);
  554.         SetTopMenuItem(0);
  555.         SetAtMenuBottom(bounds.bottom);
  556.         do {
  557.             GetMouse(&hit);
  558.             mdef(mFloatChooseMsg, (**fmenu).menu, &bounds, hit, &item);
  559.         } while (StillDown());
  560.         (**fmenu).item = item;
  561.         SetPort(port);
  562.         
  563.         if (item) {
  564.             /* execute the selected menu item */
  565.             Delay(8, &delay);
  566.             FloatMenuUnhilite(fmenu);
  567.             pick = MenuPick(FloatMenuID(fmenu), item);
  568.             EventMenu(&pick);
  569.         }
  570.     }
  571.     ensure(FloatMenuValid(fmenu));
  572. }
  573.  
  574. /* free up memory used by floating windows */
  575. void FloatMenuMemoryLow(void)
  576. {
  577.     FloatMenuHandle fmenu = NULL;
  578.     
  579.     /* Once a floating menu has been disposed of, the user can't recreate
  580.         it by tearing off the menu since the menu's defProc field will have
  581.         been restored. To prevent the menu from being completely disposed
  582.         of we first dispose of less vital blocks of memory. */
  583.     fmenu = gFloatMenuList;
  584.     while (fmenu && MemWarning() >= MEM_WARNING_VERY_LOW) {
  585.         if ((**fmenu).offscreen)
  586.             OffscreenPurge((**fmenu).offscreen);
  587.         fmenu = LLHNext(fmenu);
  588.     }
  589.     fmenu = gFloatMenuList;
  590.     while (fmenu && MemWarning() >= MEM_WARNING_VERY_LOW) {
  591.         FloatMenuClose(fmenu);
  592.         fmenu = LLHNext(fmenu);
  593.     }
  594.     while (gFloatMenuList && MemWarning() >= MEM_WARNING_CRITICAL)
  595.         FloatMenuEnd(gFloatMenuList);
  596. }
  597.